#include <string.h>
#include <stdlib.h>
#include "dblock.h"
#include "CommonDB.h"
#include "config.h"


#define DB_WRITE_MODE (S_IRUSR|S_IWUSR |S_IRGRP|S_IWGRP |S_IROTH|S_IWOTH)
#define DB_READ_MODE  DB_WRITE_MODE

//test
//#undef FLOCK_READ
//#define FLOCK_READ FLOCK_WRITE

typedef db_handle DBM;

dblock_t* dblock_open(const char* fname)
{
	dblock_t* me = (dblock_t*)calloc(1, sizeof(dblock_t));
	
    if (0 == me) return NULL;
	me->fname = strdup(fname);
	
	{
		DBM* db = dba_create(me->fname, 0, DB_CREATE, 0);
		if (db) dba_free(db);	
	}

	{
		char lock_path[256] = {0};
		char* lock_name = strrchr(fname, '/');
		if (lock_name == 0)
			lock_name = (char*)fname;	
		else
			lock_name ++;
		sprintf(lock_path, "/tmp/flock_%s", lock_name);
		TRACE("flock: %s -> %s ", fname, lock_path);

		me->locker = file_lock_new(lock_path, DB_WRITE_MODE);
		if (me->locker == 0){
			TRACE("dblock_new locker null, errstr = %s %d", strerror(errno), __LINE__);
			free(me->fname);
			free(me);
			return (dblock_t*)0;
		}
	}

	return me;
}

void dblock_close(dblock_t*me)
{
	if (me) {
		DBM* db = (DBM*)me->cursor;
		if (db) dba_free(db);	
		free(me->fname);
		me->fname = NULL;
		file_lock_delete(me->locker);
		free(me);
	}
}

int dblock_fetch(dblock_t*me, void* key, int keylen, void** pdata)
{
	int len;
	return dblock_fetch_ex(me, key, keylen, pdata, &len);
}

#define DBLOCK_LOCK(n) \
	ret = file_lock_require(me->locker, n); \
	if (ret != 0) {TRACE("file lock error!\n line:%d", __LINE__); return EDBL_LOCK;}
	
#define DBLOCK_UNLOCK file_lock_release(me->locker)

int dblock_fetch_ex(dblock_t*me, void* key, int keylen, void** pdata, int* pdatalen)
{
	int ret = -1;
	DBM* db = 0;

	DBLOCK_LOCK(FLOCK_READ);

	db = dba_create(me->fname, 0, DB_RDONLY, 0);
	if (db == 0) {
		TRACE("dblock open %s failed! [%s]", me->fname, __func__);
		ret == EDBL_FILE;
		goto __clean;
	}

	if (0 != dba_fetch_ex(db, key, keylen, pdata, pdatalen)) {
		ret = EDBL_FIND;
		goto __clean;
	}

	ret = EDBL_OK;
__clean:
	if (db) dba_free(db);	
	DBLOCK_UNLOCK;
	
	return ret;
}

/*
 * flags to dbsm_store
 */
#define DBLOCK_INSERT      0
#define DBLOCK_REPLACE     1
static int dblock_store(dblock_t*me, void* key, int keylen, void* data, int datalen, int flag)
{
	int ret = -1;
	DBM* db = 0;

	DBLOCK_LOCK(FLOCK_WRITE);

	db = dba_create(me->fname, 0, DB_CREATE, 0);
	if (db == 0) {
		TRACE("dblock open %s failed! [%s %s]", me->fname, strerror(errno), __func__);
		ret == EDBL_FILE;
		goto __clean;
	}

	if (flag==DBLOCK_INSERT)
		ret = dba_insert(db, key, keylen, data, datalen);
	else
		ret = dba_replace(db, key, keylen, data, datalen);
	if (ret != 0) {
		TRACE("dblock store %s failed! [%s %s]", me->fname, strerror(errno), __func__);
		ret = EDBL_STORE;
		goto __clean;
	}

	ret = EDBL_OK;
__clean:
	if (db) dba_free(db);	
	DBLOCK_UNLOCK;
	
	return ret;
}

int dblock_replace(dblock_t*db, void* key, int keylen, void* data, int datalen)
{
	return dblock_store(db, key, keylen, data, datalen, DBLOCK_REPLACE);
}

int dblock_insert(dblock_t*db, void* key, int keylen, void* data, int datalen) 
{
	return dblock_store(db, key, keylen, data, datalen, DBLOCK_INSERT);
}

int dblock_delete(dblock_t*me, void* key, int keylen)
{
	int ret = -1;
	DBM* db = 0;

	DBLOCK_LOCK(FLOCK_WRITE);

	db = dba_create(me->fname, 0, DB_CREATE, 0);
	if (db == 0) {
		TRACE("dblock open %s failed! [%d %s %s]", me->fname, errno, strerror(errno), __func__);
		ret == EDBL_FILE;
		goto __clean;
	}

	ret = dba_delete(db, key, keylen);
	if (ret != 0) {
		usleep(10*1000);
		ret = dba_delete(db, key, keylen);
	}
	if (ret != 0) {
		TRACE("dblock delete %s failed! [%d %s %s]", me->fname, errno, strerror(errno), __func__);
		ret = EDBL_STORE;
		goto __clean;
	}

	ret = EDBL_OK;
__clean:
	if (db) dba_free(db);	
	DBLOCK_UNLOCK;

	return ret;
}

int dblock_count(dblock_t*me, int * pcount)
{
	int ret = -1;
	DBM* db = 0;

	DBLOCK_LOCK(FLOCK_READ);

	db = dba_create(me->fname, 0, DB_RDONLY, 0);
	if (db == 0) {
		TRACE("dblock open %s failed! [%s]", me->fname, __func__);
		ret == EDBL_FILE;
		goto __clean;
	}

	ret = dba_count(db, pcount);
	if (ret != 0) {
		TRACE("dblock count %s failed! [%d %s %s]", me->fname, errno, strerror(errno), __func__);
		ret = EDBL_FIND;
		goto __clean;
	}

	ret = EDBL_OK;
__clean:
	if (db) dba_free(db);	
	DBLOCK_UNLOCK;

	return ret;
}


int dblock_opencursor(dblock_t*me)
{
	int ret = -1;
	/*DBM* db = (DBM*)me->cursor;
	
	DBLOCK_LOCK(FLOCK_READ);
	if (db) dba_free(db);	

	db = dbsm_open(me->fname, O_RDONLY, DB_READ_MODE);
	if (db == 0) {
		TRACE("dblock opencursor %s failed! [%s]", me->fname, __func__);
    	DBLOCK_UNLOCK;
		ret == EDBL_FILE;
		goto __clean;
	}

	me->cursor = db;
	ret = EDBL_OK;
	*/
__clean:
	return ret;
}

int dblock_firstkey(dblock_t*me, void** pkey, int* plen,void ** pdata,int *pdatalen)
{
	int ret = -1;
	/*DBM* db = (DBM*)me->cursor;
	dbpair ikey = {0};
	dbpair idata = {0};

	if (db == 0)
		return EDBL_PARAM;

	ikey = dbsm_firstkey(db);
	if (NULLITEM(ikey)) {
		ret = EDBL_FIND;
		goto __clean;
	}

	idata = dbsm_fetch(db, ikey);
	if (NULLITEM(idata)) {
		ret = EDBL_FIND;
		goto __clean;
	}

	*pkey = (void*)ikey.dptr;
	*plen = ikey.dsize;
	*pdata = (void*)idata.dptr;
	*pdatalen = idata.dsize;

	ret = EDBL_OK;
	*/
__clean:
	return ret;
}

int dblock_nextkey(dblock_t*me, void** pkey, int* plen,void ** pdata,int *pdatalen)
{
	int ret = -1;
	/*DBM* db = (DBM*)me->cursor;
	dbpair ikey = {0};
	dbpair idata = {0};

	if (db == 0)
		return EDBL_PARAM;

	ikey = dbsm_nextkey(db);
	if (NULLITEM(ikey)) {
		ret = EDBL_FIND;
		goto __clean;
	}

	idata = dbsm_fetch(db, ikey);
	if (NULLITEM(idata)) {
		ret = EDBL_FIND;
		goto __clean;
	}

	*pkey = (void*)ikey.dptr;
	*plen = ikey.dsize;
	*pdata = (void*)idata.dptr;
	*pdatalen = idata.dsize;

	ret = EDBL_OK;
	*/
__clean:
	return ret;
}

int dblock_closecursor(dblock_t*me)
{
	/*DBM* db = (DBM*)me->cursor;
	
	if (db) dba_free(db);	
	me->cursor = 0;
	DBLOCK_UNLOCK;
	*/

	return EDBL_OK;
}
